/*!
OC_webtest v0.0.1
Copyright 2014 OrChange Inc. All rights reserved.
Licensed under the GPL License.
*/

/**=====================
 * Models DEFINE!
*=====================*/
// Delay define.
function Delay( time ){
	this.name = "delay";

	// If no param input, make time be config.DEFAULT_DELAY .
	if ( typeof(time) === "undefined" ){
		this.time = config.DEFAULT_DELAY;
	}
	else if( typeof(time) !== "number" ){
		throw MODEL_DELAY_TIME;
	}
	else {
		this.time = time;
	}
	
	//Timer ID
	this.timer;
}
// Event define.
function Event(){
	this.name = "event";
	
	//TODO 
	this.condition = "$iframe.get(0).contentWindow._dheight == 199";
	
	//TODO config & default.
	this.overtime;
}
// Stepby define.
function Stepby(){
	this.name = "stepby";
}

/**=====================
 * Step DEFINE!
*=====================*/
var Status = {
	"READY" : -1,
	"DOING" : 2,
	"FAIL" : 0,
	"OK" : 1,
}
function Step ( operator, method ){
	// The data type of operator is a String.
	this.operator = operator;
	
	// There three models to Step. They are Delay,
	// Event and Stepby.
	if ( 
		!(method instanceof Delay) && 
		!(method instanceof Event) && 
		!(method instanceof Stepby)
	){ throw STEP_METHOD; }
	
	// Use Delay model for default.
	if ( typeof(method) === "undefined" ){
		this.method = new Delay();
	}
	else {
		this.method = method;
	}
	
	this.run_timestamp;
	
	// Init. status of this step.
	this.status = Status.READY;
};

Step.prototype = {

	// The execute function of this Step object.
	run : function(){
		// Execute the content by string in a Step.
		// You can customs set a name in config.IFRAME_HANDLE.
		
		// Run the current step!
		this.start_time = ( new Date() ).getTime();
		eval( config.IFRAME_HANDLE +".contents().find"+
			this.operator );
			
		// Refresh the status of  current step.
		this.status = Status.OK;
		//TODO full operations.
		
		return this;
	},
	
	// Highlight the DOM of operation. 
	highlight : function(){
		
		(function(that){
			_object = eval( config.IFRAME_HANDLE +
				".contents().find"+that.getObject() );
			
			// Giva a tag by time to "id" of highlight div.
			_now = (new Date()).getTime();
			
			$('body').append( 
			  '<div style="position:absolute;index:10;background:rgba(255,255,66,0.7);top:'+
			  _object.offset().top+'px;left:'+_object.offset().left+'px;width:'+
			  _object.get(0).offsetWidth+'px;height:'+
			  _object.get(0).offsetHeight+'px;" id="'+_now+'"></div>'
			);
			
			setTimeout(function(){
				$("#"+_now).remove();
			}, config.HIGHLIGHT_REMOVE_DELAY );
			
			return false;
		})(this);
		
		return this;
	},
	
	getObject : function(){
		//TODO not a correct way. Use regex
		return this.operator.split('.')[0];
	},

	resetStatus : function(){},
	checkMe : function(){},
	
}

/**=====================
 * Process DEFINE!
*=====================*/
function Process(){
	
	// Save steps.
	this.queue = new Array();
	
	// A buff of Step in runtime.
	this.cur_step;
	
	// Programe counter.
	this.counter = 0;
	
	// Current loop times.
	this.loop = 0;
	
	// Record result from each step.
	this.result = {}; 
	
	// Link dictionary.
	this.dict;
};

// Core engine function.
Process.prototype = {
	
	_core : function(){
		// If enable in config, it will be highlighted here.
		if( config.SHOW_AREA_MODE === true ){
			this.cur_step.highlight();
		}
		this.cur_step.run();
		
		// Continue to execute if not complete.
		if( ++this.counter < this.getLength() ){
			(function(that){
				that.execute();
			})(this);
		}
		
		// Reset counter for next loop. But if all
		// loops have completed, return function.
		//TODO compute real loop count.
		else if ( ++this.loop < config.DEFAULT_LOOP_COUNT ){
			// I think that the use of closure function here can
			// release the _core() method of the last period.
			(function(that){
				that.counter = 0;
				that.execute();
			})(this);
		}
		else{
			//TODO something after the whole process.
		}
	},
	
	// Execute engine.
	execute : function(){
		that = this;
		
		// Buff current step object here.
		this.cur_step = this.queue[this.counter];
		
		// Function of Time model is here. Here "this" is the 
		// window Object. So replace by "that".
		if ( this.cur_step.method instanceof Stepby || 
			config.FOCUS_STEPBY ){
			//TODO need a control panel.
			$("#StepTrigger")
				.css("background","red").click(function(){
				$(this)
					.css( "background", "green" )
					.unbind("click");
				that._core();
			});
		}
		else if ( this.cur_step.method instanceof Delay ){
		
			this.cur_step.method.timer = setTimeout(
			
				function(){
					that._core();
				},
				
				// Control the real delay by DELAY_RATIO.
				that.cur_step.method.time * 
					( config.DELAY_RATIO_MODE ? 
						config.DELAY_RATIO : 1.0 )
			);
			
		}
		else if ( this.cur_step.method instanceof Event ){
		
			var timeouter_id = setTimeout(
				function(){
					clearInterval( that.cur_step.method.timer );
					console.log("time out!");
				},
				config.EVENT_TIMEOUT
			);
			
			this.cur_step.method.timer = setInterval(
				function(){
					if( eval(that.cur_step.method.condition) ){
						clearInterval( that.cur_step.method.timer );
						clearTimeout( timeouter_id );
						that._core();
					}
				},
				config.EVENT_TEST_CYCLE
			);
			
		}
	},
	
	// Pause a runtime process.
	pause : function(){

		// Clear timers.
		if( this.cur_step.method instanceof Delay ){
			clearTimeout( this.cur_step.method.timer );
		}
		else if ( this.cur_step.method instanceof Event ){
			clearInterval( this.cur_step.method.timer );
		}
		
		return this;
	},
	
	// Add one step object to Process.queue 
	addStep : function( step ){
		try{
			// Param step should be a instance of Step.
			if( !(step instanceof Step) ){ throw ERROR_BAD_STEP; }
			
			// Add new step to queue.
			this.queue.push( step );
			
			return;
		}
		catch(e){
		}
		
		return this;
	},

	restartMe : function(){

	},

	// Return the number of steps in the process.
	getLength : function(){
		return this.queue.length;
	},

	// Delete a Step from queue.
	deleteStepByIndex : function( index ){
		// Delete a step from queue. Start from 
		// index last 1.
		return this.queue.splice( index, 1 );
	},

	// Insert a Step from queue.
	insertStepByIndex : function( index, step ){
		// Insert a step at index.
		return this.queue.splice( index, 0, step );
	},

	// Set the position of queue.
	setCounter : function( pos ){
		return this.counter = pos;
	},

	// Get the position of queue.
	getCounter : function(){
		return this.counter;
	},

	// Import a queue in string array.
	importQueueFromString : function( str_queue ){
		//TODO
		_length = str_queue.length;
		for ( i=0; i<_length; i++ ){
			tmp = str_queue[i].split("|");
			this.queue.push( new Step( tmp[0], tmp[1]-0 ) );
		}
		
		return this;
	},

	// Export the queue in string array.
	exportQueueToString : function(){
		//TODO
		str_queue = new Array();
		for ( i=0; i<this.getLength(); i++ ){
			str_queue.push( this.queue[i].operator+'|'+
				this.queue[i].delay );
		}
		return str_queue;
	},

	// Drop the whole step queue.
	destroyQueue : function(){
		return this.queue = new Array();
	},

	// Output all details by string to console.
	getDetails : function(){
		//TODO
		return this;
	},

	// Confirm this Process right.
	// May use this function before execute.
	checkMe : function( step ){
		//TODO
		return this;
	},

	resetMe : function(){
	
	},

}

// Mark test.js have been loaded successfully.
script.TEST = true;